package com.budwk.app.sqfw.services.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.PageUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.budwk.app.common.config.AppException;
import com.budwk.app.sqfw.component.CommonComponent;
import com.budwk.app.sqfw.component.PushMsgComponent;
import com.budwk.app.sqfw.constants.CommonConstants;
import com.budwk.app.sqfw.models.SqfwPushMsgLog;
import com.budwk.app.sqfw.models.Sqfw_servicerecord;
import com.budwk.app.sqfw.services.SqfwPushMsgLogService;
import com.budwk.app.sqfw.vo.pushmsg.InOutPushVo;
import com.budwk.app.sqfw.vo.pushmsg.PushMsgConfigVo;
import com.budwk.app.sqfw.vo.pushmsg.PushMsgContentVo;
import com.budwk.app.sys.models.Sys_unit;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.services.SysUnitService;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.util.StrUtil;
import com.budwk.app.util.poiUtils.nutzBeanUtils.NutzIocUtils;
import com.budwk.starter.common.constant.RedisConstant;
import com.budwk.starter.common.page.Pagination;
import com.budwk.starter.common.result.Result;
import com.budwk.starter.database.service.BaseServiceImpl;
import com.budwk.starter.security.utils.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author ZY.Wang
 * @since 2023/7/4
 */
@IocBean(args = {"refer:dao"})
@Slf4j
public class SqfwPushMsgLogServiceImpl extends BaseServiceImpl<SqfwPushMsgLog> implements SqfwPushMsgLogService {
    public SqfwPushMsgLogServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private PushMsgComponent pushMsgComponent;
    @Inject
    private SysUserService userService;
    @Inject
    private RedisService redisService;
    @Inject("taskQueryExecutor")
    private ThreadPoolExecutor taskQueryExecutor;

    @Override
    public Result<?> failMsgRetry(String msgId) {
        SqfwPushMsgLog sqfwPushMsgLog = this.fetch(msgId);
        if (sqfwPushMsgLog == null) {
            throw new AppException("重试失败：查询不到对应的失败消息");
        }
        if (sqfwPushMsgLog.getIsSuccess()) {
            throw new AppException("该消息已经发送成功，无需重试");
        }
        String pushMsgConfig = sqfwPushMsgLog.getPushMsgConfig();
        PushMsgConfigVo config = JSONObject.parseObject(pushMsgConfig, PushMsgConfigVo.class);
        // 发送消息
        PushMsgContentVo c = JSONObject.parseObject(sqfwPushMsgLog.getContextData(), PushMsgContentVo.class);
        boolean isSuccess = pushMsgComponent.sendMsg(config, c.getContent());
        if (!isSuccess) {
            throw new AppException("重试失败，请联系系统管理员");
        }
        sqfwPushMsgLog.setIsSuccess(true).setUpdatedAt(System.currentTimeMillis());
        this.updateIgnoreNull(sqfwPushMsgLog);
        return Result.success();
    }

    @Override
    public void pushMessage(PushMsgConfigVo c, Sys_user recUser,
                            Sqfw_servicerecord serviceRecord) {
        // 1 检查是否满足发送
        if (!pushMsgComponent.isPushMsg(c)) {
            return;
        }
        // 2 发送消息
        // 获取发送内容体
        PushMsgContentVo msgVo = pushMsgComponent.getPushContent(c.getAction(), recUser, serviceRecord);
        boolean isSuccess = pushMsgComponent.sendMsg(c, msgVo.getContent());
        Sys_user pushUser = CommonComponent.getCurrentUser();
        String pushBatchNo = String.valueOf(System.currentTimeMillis());
        if (!isSuccess) {
            pushMsgComponent.retry(c, recUser, pushUser, msgVo, PushMsgComponent.MSG_TYPE_SYSTEM, pushBatchNo);
            return;
        }
        pushMsgComponent.insertSuccessMsgLog(c, recUser, pushUser, msgVo, PushMsgComponent.MSG_TYPE_SYSTEM, pushBatchNo);
    }

    @Override
    public void pushAppMessage(SqfwPushMsgLog msgLog) {
        if (StringUtils.isEmpty(msgLog.getContextData())) {
            throw new AppException("发送内容体参数不能为空！");
        }
        // 2 发送消息
        // 构建发送人
        Sys_user pushUser = CommonComponent.getCurrentUser();
        List<String> receiveIds = List.of(msgLog.getReceiveId().split(","));
        List<Sys_user> reUsers = userService.query(Cnd.where(Sys_user::getId, "in", receiveIds));
        CompletableFuture.runAsync(() -> pushMsg(msgLog, reUsers, pushUser), taskQueryExecutor);
    }

    private void pushMsg(SqfwPushMsgLog msgLog, List<Sys_user> receiveUsers, Sys_user pushUser) {
        String pushBatchNo = String.valueOf(System.currentTimeMillis());
        for (Sys_user receiveUser : receiveUsers) {
            log.info("pushAppMessage param: {}", JSONObject.toJSONString(msgLog));
            // 构建发送者基本信息
            msgLog.setId(StrUtil.getUUID()).setReceiveUserName(receiveUser.getUsername()).setReceiveUserPhone(receiveUser.getMobile());
            // 发送内容体
            PushMsgContentVo msgVo = JSONObject.parseObject(msgLog.getContextData(), PushMsgContentVo.class);
            msgVo.setPushDate(LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN))
                    .setPusherText("来自" + pushUser.getUsername() + "的信息");
            PushMsgConfigVo c = new PushMsgConfigVo();
            c.setSqfwId(msgLog.getId()).setAction("push_msg_person");
            // 开发发送信息
            boolean isSuccess = startPushMsg(receiveUser, pushUser, pushBatchNo, msgVo, c);
//            boolean isSuccess = true;
            if (isSuccess) {
                pushMsgComponent.insertSuccessMsgLog(c, receiveUser, pushUser, msgVo, PushMsgComponent.MSG_TYPE_ERPSON, pushBatchNo);
            }
        }
    }

    private boolean startPushMsg(Sys_user receiveUser, Sys_user pushUser, String pushBatchNo, PushMsgContentVo msgVo, PushMsgConfigVo c) {
        // 获取token
        Map<String, String> mapToken = redisService.hgetAll(RedisConstant.PRE + "push:token:" + receiveUser.getLoginname());
        String tokenStr = JSONObject.toJSONString(mapToken);
        if (StringUtils.isEmpty(receiveUser.getPushMobileInfo()) || !receiveUser.getPushMobileInfo().equals(tokenStr)) {
            if (mapToken == null || mapToken.size() == 0) {
                log.error("发送者手机型号未获取到  --------------: {}", JSONObject.toJSONString(receiveUser));
                pushMsgComponent.insertFailMsgLog(c, receiveUser, pushUser, msgVo, PushMsgComponent.MSG_TYPE_ERPSON, pushBatchNo);
                return false;
            }
            receiveUser.setPushMobileInfo(tokenStr);
            userService.updateIgnoreNull(receiveUser);
        }
        JSONObject pushJson = JSONObject.parseObject(receiveUser.getPushMobileInfo());
        c.setPushChannel(pushJson.getString(PushMsgComponent.PUSH_CHANNEL)).setPushToken(pushJson.getString(PushMsgComponent.PUSH_TOKEN));
        // 发送消息
        boolean isSuccess = pushMsgComponent.sendMsg(c, msgVo.getContent());
        if (!isSuccess) {
            pushMsgComponent.insertFailMsgLog(c, receiveUser, pushUser, msgVo, PushMsgComponent.MSG_TYPE_ERPSON, pushBatchNo);
            return false;
        }
        return true;
    }

    @Override
    public void replyMessage(SqfwPushMsgLog msgLog) {
        SqfwPushMsgLog pushMsgLog = this.fetch(msgLog.getId());
        if (pushMsgLog == null) {
            throw new AppException("查询不到对应的失败消息");
        }
        String replyContent = msgLog.getReplyContent();
        if (StringUtils.isEmpty(replyContent)) {
            throw new AppException("回复内容不能为空！");
        }
        PushMsgContentVo msgVo = JSONObject.parseObject(replyContent, PushMsgContentVo.class);
        msgVo.setPushDate(LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN));
        if (StringUtils.isEmpty(pushMsgLog.getReplyContent())) {
            List<PushMsgContentVo> paramList = new ArrayList<>();
            paramList.add(msgVo);
            pushMsgLog.setReplyContent(JSONObject.toJSONString(paramList));
        } else {
            List<PushMsgContentVo> paramList = JSONArray.parseArray(pushMsgLog.getReplyContent(), PushMsgContentVo.class);
            paramList.add(msgVo);
            pushMsgLog.setReplyContent(JSONObject.toJSONString(paramList));
        }
        pushMsgLog.setRead(true).setUpdatedBy(SecurityUtil.getUserId());
        pushMsgLog.setUpdatedAt(System.currentTimeMillis());
        this.updateIgnoreNull(pushMsgLog);
    }

    @Override
    public Pagination listMsgByPage(SqfwPushMsgLog msgLog) {
        if (msgLog == null) {
            throw new AppException("对象参数不能为空！");
        }
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup where = cnd.where();
        where.andEquals(SqfwPushMsgLog::getIsSuccess, true);
        cnd.getOrderBy().desc(SqfwPushMsgLog::getCreatedAt);
        // 收件箱 只查询成功的信息
        if ("1".equals(msgLog.getPushType())) {
            return queryInBoxData(msgLog);
        }
        // 发件箱 只查询成功的信息
        if ("2".equals(msgLog.getPushType())) {
            return queryOutBoxData(msgLog);
        }
        Pagination pagination = this.listPage(msgLog.getPageNo(), msgLog.getPageSize(), cnd);
        return pagination;
    }

    // 发件箱
    private Pagination queryOutBoxData(SqfwPushMsgLog msgLog) {
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup where = cnd.where();
        where.andLike(SqfwPushMsgLog::getPushUserId, SecurityUtil.getUserId())
                .andEquals(SqfwPushMsgLog::getIsSuccess, true)
                .andEquals(SqfwPushMsgLog::getMsgType, PushMsgComponent.MSG_TYPE_ERPSON); // 系统发的消息不在此范围
        cnd.getOrderBy().desc(SqfwPushMsgLog::getCreatedAt);
        // 查询符合条件的所有数据
        List<SqfwPushMsgLog> allList = this.query(cnd);
        if (CollectionUtil.isEmpty(allList)) {
            return null;
        }
        // 对批次号去重
        int pageNo = msgLog.getPageNo();
        int pageSize = msgLog.getPageSize();
        List<String> batchNo = allList.stream().map(SqfwPushMsgLog::getPushBatchNo).distinct().collect(Collectors.toList());
        // 获取分页数据
        List<String> batchData = batchNo.stream().distinct().skip((long) (pageNo - 1) * pageSize).limit(pageSize).collect(Collectors.toList());
        // 取出批次号中的数据
        List<SqfwPushMsgLog> resData = new ArrayList<>();
        for (String batchDatum : batchData) {
            SqfwPushMsgLog msgLog1 = allList.stream().filter(e -> e.getPushBatchNo().equals(batchDatum)).findFirst().get();
            resData.add(msgLog1);
        }
        Pagination p = new Pagination();
        p.setList(resData);
        p.setTotalCount(batchNo.size());
        p.setPageSize(pageSize);
        p.setPageNo(pageNo);
        return p;
    }

    // 收件箱
    private Pagination queryInBoxData(SqfwPushMsgLog msgLog) {
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup where = cnd.where();
        where.andLike(SqfwPushMsgLog::getReceiveId, SecurityUtil.getUserId())
                .andEquals(SqfwPushMsgLog::getIsSuccess, true);
        cnd.getOrderBy().desc(SqfwPushMsgLog::getCreatedAt);
        Pagination pagination = this.listPage(msgLog.getPageNo(), msgLog.getPageSize(), cnd);
        return pagination;
    }

    @Override
    public void deleteMessage(String msgId, String pushBatchNo) {
        if (StringUtils.isNotEmpty(msgId)) {
            SqfwPushMsgLog msgLog = this.fetch(msgId);
            if (msgLog == null) {
                throw new AppException("消息不存在！");
            }
            this.delete(msgId);
            return;
        }
        if (StringUtils.isNotEmpty(pushBatchNo)) {
            List<SqfwPushMsgLog> list = this.query(Cnd.where(SqfwPushMsgLog::getPushBatchNo, "=", pushBatchNo));
            if (CollectionUtil.isEmpty(list)) {
                throw new AppException("消息不存在！");
            }
            List<String> ids = list.stream().map(SqfwPushMsgLog::getId).collect(Collectors.toList());
            this.delete(ids);
        }
    }

    /**
     * 按照五级向下推送消息
     * @param msgLog
     */
    @Override
    public void pushPowerMessage(SqfwPushMsgLog msgLog) {
        msgLog.setId(StrUtil.getUUID());
        if (StringUtils.isEmpty(msgLog.getContextData())) {
            throw new AppException("发送内容体参数不能为空！");
        }
        // 2 发送消息
        Sys_user pushUser = CommonComponent.getCurrentUser();
        // 获取当前用户下的所有用户信息
        List<Sys_user> reUsers = getReceiveUser(pushUser);
        if (CollectionUtil.isEmpty(reUsers)) {
            log.error("---------------------未查询到发送用户信息!");
            throw new AppException("未查询到发送用户信息！");
        }
        CompletableFuture.runAsync(() -> pushMsg(msgLog, reUsers, pushUser), taskQueryExecutor);
    }

    @Override
    public InOutPushVo statisticsInOutboxNumber() {
        // 收件箱数量
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup where = cnd.where();
        where.andLike(SqfwPushMsgLog::getReceiveId, SecurityUtil.getUserId()).andEquals(SqfwPushMsgLog::getIsSuccess, true);

        // 发件箱数量
        Cnd cnd1 = Cnd.NEW();
        SqlExpressionGroup where1 = cnd1.where();
        where1.andLike(SqfwPushMsgLog::getPushUserId, SecurityUtil.getUserId()).andEquals(SqfwPushMsgLog::getIsSuccess, true);

        InOutPushVo inOutPushVo = new InOutPushVo();
        inOutPushVo.setInNumber(this.count(cnd)).setOutNumber(this.count(cnd1));

        return inOutPushVo;
    }

    @Override
    public List<Sys_user> queryUnitAllUser(String unitId) {
        SysUnitService unitService = NutzIocUtils.getBean(SysUnitService.class);
        List<Sys_user> allUnitUser = new ArrayList<>();
        unitService.queryUnitTree(unitId, allUnitUser);
        return allUnitUser;
    }

    private List<Sys_user> getReceiveUser(Sys_user pushUser) {
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup where = cnd.where();
        handleUserPer(pushUser, where);
        List<Sys_user> receiveUsers = userService.query(cnd);
        return receiveUsers;
    }

    /**
     * 当前用户权限
     * @param pushUser
     * @param where
     */
    private void handleUserPer(Sys_user pushUser, SqlExpressionGroup where) {
        if (StringUtils.isNotEmpty(pushUser.getProvince()) && CommonConstants.DIRECTLY_UNDER.equals(pushUser.getProvince())) {
            return;
        }
        where.andEquals(Sys_user::getProvince, pushUser.getProvince());
        if (StringUtils.isNotEmpty(pushUser.getCity()) && CommonConstants.DIRECTLY_UNDER.equals(pushUser.getCity())) {
            return;
        }
        where.andEquals(Sys_user::getCity, pushUser.getCity());
        if (StringUtils.isNotEmpty(pushUser.getDistrict()) && CommonConstants.DIRECTLY_UNDER.equals(pushUser.getDistrict())) {
            return;
        }
        where.andEquals(Sys_user::getDistrict, pushUser.getDistrict());
        if (StringUtils.isNotEmpty(pushUser.getCounty()) && CommonConstants.DIRECTLY_UNDER.equals(pushUser.getCounty())) {
            return;
        }
        where.andEquals(Sys_user::getCounty, pushUser.getCounty());
        if (StringUtils.isNotEmpty(pushUser.getCommunity()) && CommonConstants.DIRECTLY_UNDER.equals(pushUser.getCommunity())) {
            return;
        }
        where.andEquals(Sys_user::getCommunity, pushUser.getCommunity());
    }
}
